package com.wise.service.impl;

import com.wise.domain.DemoEntity;
import com.wise.repository.DemoRepository;
import com.wise.service.DemoService;
import com.wise.transactionsyn.sample2.AfterCommitExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.concurrent.ThreadLocalRandom;

/**
 * 1.在需要事务管理的地方加@Transactional 注解。@Transactional 注解
 * 可以被应用于接口定义和接口方法、类定义和类的 public 方法上。
 * <p>
 * 2.@Transactional 注解只能应用到 public 可见度的方法上。 如果你在 protected、private
 * 或者 package-visible 的方法上使用 @Transactional 注解，它也不会报错，但是这个被注解的
 * 方法将不会展示已配置的事务设置。
 * <p>
 * 3.注意仅仅 @Transactional 注解的出现不足于开启事务行为，它仅仅是一种元数据。必须在配置
 * 文件中使用配置元素，才真正开启了事务行为。
 * <p>
 * 4.通过 元素的 “proxy-target-class” 属性值来控制是基于接口的还是基于类的代理被创建。如
 * 果 “proxy-target-class” 属值被设置为 “true”，那么基于类的代理将起作用（这时需要CGLIB
 * 库cglib.jar在CLASSPATH中）。如果 “proxy-target-class” 属值被设置为 “false” 或者这个
 * 属性被省略，那么标准的JDK基于接口的代理将起作用。
 * <p>
 * 5.Spring团队建议在具体的类（或类的方法）上使用 @Transactional 注解，而不要使用在类所要
 * 实现的任何接口上。在接口上使用 @Transactional 注解，只能当你设置了基于接口的代理时它才生
 * 效。因为注解是 不能继承的，这就意味着如果正在使用基于类的代理时，那么事务的设置将不能被基于
 * 类的代理所识别，而且对象也将不会被事务代理所包装。
 * <p>
 * 6.@Transactional 的事务开启 ，或者是基于接口的或者是基于类的代理被创建。所以在同一个类中
 * 一个方法调用另一个方法有事务的方法，事务是不会起作用的。
 */
@Service
@Transactional
public class DemoServiceImpl implements DemoService {
    @Resource
    private DemoRepository demoRepository;

    @Override
    public void save(DemoEntity entity) {
        demoRepository.save(entity);
    }

    @Override
    public void rollBackTest(DemoEntity entity) {
        String str = null;
        save(entity);
        //触发空指针异常进行回滚,如果事务生效，则数据库不会插入该实体
        if (str.equals("")) {
            System.out.println("触发空指针异常进行回滚,如果事务生效，则数据库不会插入该实体");
        }
    }

    /**
     * 下面为使用transactionSynchronization的业务插入方法，会执行同步器相应方法
     *****************************************/
    @Resource
    private AfterCommitExecutor afterCommitExecutor;

    /**
     * 事务方法
     *
     * @param entity
     */
    @Override
    public void insertTranSynManager(DemoEntity entity) {
        //持久化实体类(这里是调用的持久层)
        demoRepository.save(entity);
        afterCommitExecutor.execute(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(5 * 1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("execute send email task after transaction commit");
            }
        });
        /*threadPool.execute(()->{
                try {
                    Thread.sleep(5*1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("send email after transactioin commit");
            });*/

        //async work but tx not work, execute even when tx is rollback
        //asyncService.executeAfterTxComplete();

        ThreadLocalRandom random = ThreadLocalRandom.current();
        if (random.nextInt() % 2 == 0) {
            //发生异常后不会再执行execute任务 且事务回滚 数据库不会插入数据
            throw new RuntimeException("test email transaction complex");
        }
        System.out.println("service end");
    }

    @Override
    public DemoEntity queryOne(Integer id) {
        return demoRepository.findById(id).get();
    }
}
